#!/bin/sh

# LICENSE: WTFPLv2
# Don't trust google! You may do with this shit what the fuck do you want.

c_version="1.11";           # constant
c_standart="1r.4"           # constant
c_header_signature="pngfy"; # constant
n_bpp=3;                    # bytes used to store one pixel
n_depth=8;                  # image colors depth
l_mode="";                  # "encode","decode","info","format-check","format-encode","format-decode","help"
l_format="false";           # "true","false"
l_gzip="false";             # "true","false"
l_input_stdin="false";      # "true","false"
l_output_stdin="false";     # "true","false"
l_verbose="false";          # "true","false"
l_bpp_changed="false"       # "true","false"
l_depth_changed="false"     # "true","false"
l_remove="false";           # "true","false"
l_force_header="false";     # "true","false"
l_quiet="false";            # "true","false"
s_special_header="";        # user-specified header
p_encoded_file_name="";     # filename
p_input_file="";            # filepath
p_output_file="";           # filepath
p_temp_dir="/tmp"           # dirpath. use '.' for current.

used_binaries="dd cat convert identify sed awk gzip md5sum";

# checks if specified binary existed as executable
in_system() {	
(
  program_name="$1";
  case "${PATH}" in
    (*[!:]:) PATH="${PATH}:"; ;;
  esac;
  IFS=":";
  for part_of_path in ${PATH}; do
    part_of_path="${part_of_path:-"."}";
    guess_path="${part_of_path}/${program_name}";
    if [ -f "${guess_path}" ] && [ -x "${guess_path}" ]; then
      return 0;
    fi;
  done;
  return 1;
)
};

# checks if binaries from used_binaries variable are accessable for executing
init() {
(
  progs="";
  for prog in $used_binaries; do
    in_system "$prog" || { progs="$progs, '$prog'"; };
  done;
  if [ "$progs" ]; then
    printf "%s\n" "$0: binaries $(printf "%s" "$progs" | sed 's/^[ ,]*//g') are needed";
    return 1;
  fi;
)
};


# verbose message
verb_message() {
(
  message="$@";
  if [ "$l_verbose" = "true" ]; then
    printf "%s" "$message" 1>&2;
  fi;
)
};

error_message() {
(
  format="$1";
  message="$2";
  if [ "$l_quiet" = "false" ]; then
    printf "$format" "$message" 1>&2;
  fi;
)
};


# parsing args. not using getopts because i want long options. not using getopt
# because it sucks
parse_args() {
  if [ ! "$1" ]; then
    set -- "--help";
  fi;
  while [ "$1" ]; do
    case "$1" in
      ("-?"|"-h"|"-help"|"--help") l_mode="help"; return 0; ;;
      ("-e"|"--encode")  l_mode="encode"; ;;
      ("-d"|"--decode")  l_mode="decode"; ;;
      ("-ef"|"--encode-format") l_format="true"; l_mode="encode"; ;;
      ("-df"|"--decode-format") l_format="true"; l_mode="decode"; ;;
      ("-fc"|"--format-check") l_verbose="true"; l_format="true"; l_mode="format-check"; ;;
      ("-fe"|"--format-encode") l_verbose="true"; l_format="true"; l_mode="format-encode"; ;;
      ("-fd"|"--format-decode") l_verbose="true"; l_format="true"; l_mode="format-decode"; ;;
      ("-i"|"--info")  l_mode="info"; ;;
      ("-z"|"--gzip")  l_gzip="true"; ;;
      ("-v"|"--verbose")  l_verbose="true"; ;;
      ("-q"|"--quiet")  l_quiet="true"; ;;
      ("-r"|"--remove")  l_remove="true"; ;;
      ("-k"|"--force-header") l_force_header="true"; ;;
      ("-b"|"-bpp"|"--byte-per-pixel") l_bpp_changed="true"; n_bpp="$(printf "%d" "$2" 2>/dev/null)"; [ "$2" ] && { shift; }; ;;
      ("-t"|"--depth") l_depth_changed="true"; n_depth="$(printf "%d" "$2" 2>/dev/null)"; [ "$2" ] && { shift; }; ;;
      ("-n"|"--name") p_encoded_file_name="$2"; [ "$2" ] && { shift; }; ;;
      ("-s"|"--special-header") s_special_header="$2"; [ "$2" ] && { shift; }; ;;
      ("-m"|"-tmp"|"--temp-dir") p_temp_dir="$2"; [ "$2" ] && { shift; }; ;;
      (*)
        if [ ! "$p_input_file" ]; then
          p_input_file="$1";
        elif [ ! "$p_output_file" ]; then
          p_output_file="$1";
        fi;
      ;;
    esac;
    shift;
  done;

  if [ "$p_input_file" = "-" ]; then
    if [ ! "$l_mode" ]; then
      cat >/dev/null;
      error_message "%s\n" "$0: <stdin>: when reading from stdin, -e or -d must be specified";
      return 1;
    fi;
    if [ "$l_mode" = "encode" ]; then
      if [ ! "$p_output_file" ]; then
        p_output_file="-";
      fi;
    fi;
    l_input_stdin="true";
    l_remove="true";
    stdin_file="$p_temp_dir/pngfy-$$-stdin";
    cat > "$stdin_file";
    p_input_file="$stdin_file";
  else
    if [ ! "$l_mode" ]; then
      input_ext="${p_input_file##*.}";
      output_ext="${p_output_file##*.}";
      if [ "$input_ext" = "png" ]; then
        l_mode="decode";
      else
        l_mode="encode";
      fi;
      if [ "$output_ext" = "png" ]; then
       l_mode="encode";
      fi;
    fi;
    if [ ! -f "$p_input_file" ]; then
      if [ "$p_input_file" ]; then
        error_message "%s\n" "$0: $p_input_file: no such file";
        return 3;
      else
        l_mode="help";
        return 0;
      fi;
    fi;
  fi;
  if [ "$l_mode" = "encode" ] && [ ! "$p_output_file" ]; then
    p_output_file="$p_input_file.png";
  fi;
  if [ "$p_output_file" = "-" ]; then
   l_output_stdin="true";
  fi;
  if [ ! -d "$p_temp_dir" ]; then
    error_message "%s\n" "$0: $p_temp_dir: not a directory";
    return 4;
  fi;
  if [ "$l_depth_changed" = "true" ] && [ "$l_bpp_changed" = "false" ]; then
    case "$n_depth" in
      ("8") n_bpp="3"; ;;
      ("16") n_bpp="6"; ;;
    esac;
  fi;
};

# let's call this easter egg
help() {
(
  printf "%s" "\
encoder/decoder for files into png v$c_version for pngfy version $c_standart
  USAGE: $0 [OPTIONS] <input file> [output file]
    modes:
    -e  --encode                    encode file to image
    -d  --decode                    decode file from image
    -ef --encode-format             only format file to, do not encode
    -df --decode-format             only decode from file, do not format
    -fc --format-check              check if format file properly formed
    -fe --format-encode             encode formatted file
    -fd --format-decode             extract data from formatted file
    -h  --help                      display this help message
    -i  --info                      show information about file
    -v  --verbose                   display additional information messages
    -q  --quiet                     do not display anything
    -r  --remove                    remove input file
    -z  --gzip                      pass encoding/decoding files throigh gzip
    -k  --force-header              do not stop if special header is bad
    -n  --name            <string>  specify encoded file name
    -s  --special-header  <string>  specify special header
    -b  --byte-per-pixel  <int>     bytes per pixel
    -t  --depth           <int>     image depth
    -m  --temp-dir        <path>    temprory directory path
";
)
};


# parsing header from bla1|bla2|bla3|bla4 to var1=bla1; var2=bla2; etc; view
parse_header() {
  header="$1";
  set_fields() {
    header_length="$((${#header}+1))";
    header_signature="$(printf "%s" "$1" | sed -n '1p')";
    if [ "$header_signature" != "$c_header_signature" ]; then
      error_message "%s\n" "$0: $input_file: not valid header signature";
      verb_message "\
$0: header..........: $header
$0: header signature: $header_signature
";
      if [ "$l_force_header" = "false" ]; then
        return 1;
      fi
    fi;

    header_file_name="$(printf "%s" "$1" | sed -n '2p')";

    header_data_length="$(printf "%s" "$1" | sed -n '3p' | awk '{printf "%d", $1}')";

    header_is_gzip="$(printf "%s" "$1" | sed -n '4p' | awk '{printf "%d", $1}')";
    if [ "$header_is_gzip" != "0" ] && [ "$header_is_gzip" != "1" ]; then
    error_message "%s\n" "$0: $input_file: not a valid file: gzip flag have wrong value";
    verb_message "\
$0: header..........: $header
$0: header gzip flag: $header_is_gzip
";
      if [ "$l_force_header" = "false" ]; then
        return 1;
      fi
    fi

    header_data_md5="$(printf "%s" "$1" | sed -n '5p')";
    if [ "$(printf "%s" "$header_data_md5" | sed 's/[^a-fA-F0-9]//g' | wc -c | awk '{printf "%d", $1}')" -ne 32 ]; then
    error_message "%s\n" "$0: $input_file: not a valid file: file md5 have wrong value";
    verb_message "\
$0: header..........: $header
$0: header data md5: $header_data_md5
";
      if [ "$l_force_header" = "false" ]; then
        return 1;
      fi
    fi

    if [ "$header_is_gzip" = "1" ]; then
      if [ "$header_params_length" -lt 5 ]; then
        error_message "%s\n" "$0: $input_file: not a valid file: header is not properly formed";
        verb_message "\
$0: header..........: $header
$0: header params: $header_params_length
";
      fi;
      header_gzip_md5="$(printf "%s" "$1" | sed -n '6p')";
      if [ "$(printf "%s" "$header_gzip_md5" | sed 's/[^a-fA-F0-9]//g' | wc -c | awk '{printf "%d", $1}')" -ne 32 ]; then
      error_message "%s\n" "$0: $input_file: not a valid file: gzip md5 have wrong value";
      verb_message "\
$0: header..........: $header
$0: header gzip md5: $header_gzip_md5
";
      if [ "$l_force_header" = "false" ]; then
        return 1;
      fi
      fi;
    else
      if [ "$header_params_length" -gt 4 ]; then
        error_message "%s\n" "$0: $input_file: not a valid file: header is not properly formed";
        verb_message "\
$0: header..........: $header
$0: header params: $header_params_length
";
        if [ "$l_force_header" = "false" ]; then
          return 1;
        fi
      fi;
    fi;
  };

  header_params="$(printf "%s" "$header" | sed 's/|/\n/g')";
  header_params_length="$(printf "%s" "$header_params" | wc -l)";

  if [ "$header_params_length" -lt 4 ] || [ "$header_params_length" -gt 5 ]; then
    error_message "%s\n" "$0: $input_file: not a valid file: header is not properly formed";
    verb_message "\
$0: header..........: $header
$0: header params: $header_params_length
";
      if [ "$l_force_header" = "false" ]; then
        return 1;
      fi
  fi;

  set_fields "$header_params" || {
    return "$?";
  };

  unset header_params;
  unset header_params_length;
};


# encode file to image
encode_file() {
(
  input_file="$1";
  output_file="$2";

  tmp_file="$p_temp_dir/pngfy-$$-temp";
  tmp_gzip_file="$p_temp_dir/pngfy-$$-gzip";

  header_length="0";
  header_file_name="";
  header_data_length="0";
  header_is_gzip="0";
  header_data_md5="";
  header_gzip_md5="";

  tmp_length="0";
  tmp_zeros_length="0";
  tmp_pixels="0";

  image_pixels="0";
  image_x="0";
  image_y="0";
  image_depth="$n_depth";
  image_bpp="$n_bpp";

  verb_input_file_name="$input_file";
  verb_output_file_name="$output_file";
  verb_gziped="false";

  if [ "$l_input_stdin" = "true" ]; then
    verb_input_file_name="<stdin>";
  fi;
  if [ "$l_output_stdin" = "true" ]; then
    verb_output_file_name="<stdout>";
  fi;
  verb_message "\
encoding file '$verb_input_file_name' to '$verb_output_file_name'
";

# header
  if [ ! "$s_special_header" ] && [ "$l_force_header" != "true" ]; then
    if [ "$p_encoded_file_name" ]; then
      header_file_name="$p_encoded_file_name";
    else
      header_file_name="${input_file##*/}";
    fi;

    header_data_md5="$(md5sum "$input_file" | awk '{printf "%s", $1}')";

    if [ "$l_gzip" = "true" ]; then
      header_is_gzip="1";
      verb_gziped="true";
      cat "$input_file" | gzip -n -9 >> "$tmp_gzip_file";
      input_file_name="$input_file_name";
      header_gzip_md5="$(md5sum "$tmp_gzip_file" | awk '{printf "%s", $1}')"
      input_file="$tmp_gzip_file";
    fi;

    header_data_length="$(wc -c "$input_file" | awk '{printf "%s", $1}')";

    header="pngfy|$header_file_name|$header_data_length|$header_is_gzip|$header_data_md5";
    if [ "$header_is_gzip" = "1" ]; then
      header="$header|$header_gzip_md5";
    fi;
    header_length="$((${#header}+1))";
  else
    header="$s_special_header";
    parse_header "$header" || {
      return "$?";
    };
    if [ "$header_is_gzip" = "1" ]; then
      verb_gziped="true";
      cat "$input_file" | gzip -n -9 >> "$tmp_gzip_file";
      input_file_name="$input_file_name";
      header_gzip_md5="$(md5sum "$tmp_gzip_file" | awk '{printf "%s", $1}')"
      input_file="$tmp_gzip_file";
    fi;
  fi;

# tmp

  printf "%s\0" "$header" | cat - "$input_file" > "$tmp_file";

  tmp_length="$(wc -c "$tmp_file" | awk '{printf "%s", $1}')";


  if [ "$header_is_gzip" = "1" ]; then
    rm -f "$tmp_gzip_file";
  fi;

  tmp_lack_length="$(($tmp_length % $image_bpp))";
  if [ "$tmp_lack_length" -gt 0 ]; then
    diff="$(($image_bpp - $tmp_lack_length))";
    tmp_zeros_length="$(($tmp_zeros_length + $diff))";
    tmp_length="$(($tmp_length + $diff))";
  fi;

# image

  tmp_pixels="$(($tmp_length / $image_bpp))";
  tmp_pixels_sqrt="$(printf "%d" "$tmp_pixels" | awk '{printf "%d", sqrt($1);}')";
  image_x="$tmp_pixels_sqrt";
  image_y="$tmp_pixels_sqrt";

  image_pixels="$(($image_x * $image_y))";
  while [ "$image_pixels" -lt "$tmp_pixels" ]; do
    image_x="$(($image_x + 1))";
    image_pixels="$(($image_x * $image_y))";
  done;

# tmp

  if [ "$image_pixels" -gt "$tmp_pixels" ]; then
    diff="$((( $image_pixels - $tmp_pixels ) * $image_bpp))";
    tmp_zeros_length="$(($tmp_zeros_length + $diff))";
    tmp_length="$(($tmp_length + $diff))";
  fi;

# filling

  dd ibs=1 count="$tmp_zeros_length" if="/dev/zero" >> "$tmp_file" 2>/dev/null;

# outs

  verb_info_message="\
encoded successfuly
header............: $header
image size........: ${image_x}x${image_y}
image depth.......: $image_depth
header length.....: $header_length
header file name..: $header_file_name
header data length: $header_data_length
header gziped.....: $verb_gziped
header file md5...: $header_data_md5
";
  if [ "$header_is_gzip" = "1" ]; then
    verb_info_message="${verb_info_message}\
gzip md5..........: $header_gzip_md5
";
  fi;

  if [ "$l_format" = "true" ]; then
    if [ "$l_output_stdin" = "true" ]; then
      cat "$tmp_file";
    else
      cat "$tmp_file" > "$output_file";
    fi;
    verb_message "$verb_info_message";
    if [ "$l_remove" = "true" ]; then
      rm -f "$p_input_file";
    fi;
    rm -f "$tmp_file";
    return 0;
  fi

  convert -size "${image_x}x${image_y}" -depth "$image_depth" "rgb:$tmp_file" "png:$output_file" && {
    verb_message "$verb_info_message";
    if [ "$l_remove" = "true" ]; then
      rm -f "$p_input_file";
    fi;
  }
  rm -f "$tmp_file";
)
};

# read header from file
read_header() {
  header="$1";

  if [ ! "$header" ]; then
    header_signature="$(dd if="$res_file" ibs=1 count=5 2>/dev/null)";
    if [ "$header_signature" != "$c_header_signature" ]; then
      error_message "%s\n" "$0: $input_file: not valid header signature";
      verb_message "\
$0: header..........: $header
$0: header signature: $header_signature
";
      return 1;
    fi;
    header="$(cat "$res_file" | sed 's/\x0.*//g' | ( read header; printf "%s" "$header"; ))";
  fi;

  parse_header "$header" || {
    return "$?";
  };

  file_length="$(wc -c "$res_file" | awk '{printf "%s", $1}')";
  if [ "$(($file_length - $header_length))" -lt "$(($header_data_length))" ]; then
    error_message "%s\n" "$0: $input_file: not a valid file: too short for data length";
    verb_message "\
$0: header length.....: $header_length
$0: header data length: $header_data_length
$0: file length.......: $file_length
";
    return 1;
  fi;

};


# decode image to file
decode_file() {
(
  input_file="$1";
  output_file="$2";

  file_length="0";

  res_file="";
  tmp_file="$p_temp_dir/pngfy-$$-temp";
  tmp_gzip_file="$p_temp_dir/pngfy-$$-gzip";

  header="";
  header_length="0";
  header_file_name="";
  header_data_length="0";
  header_is_gzip="0";
  header_data_md5="";
  header_gzip_md5="";

  verb_input_file_name="$input_file";
  verb_output_file_name="";
  verb_gziped="false";

  if [ "$l_input_stdin" = "true" ]; then
    verb_input_file_name="<stdin>";
  fi;

  res_file="$p_temp_dir/pngfy-$$-res";
  identify "png:$input_file" 2>/dev/null >/dev/null || {
    error_message "%s\n" "$0: $input_file: not a valid png file";
    return 1;
  };

  convert -depth "$n_depth" "png:$input_file" "rgb:$res_file" || {
    error_message "%s\n" "$0: $input_file: not a valid png file";
    return 1;
  };
  
  read_header "$s_special_header" || {
    ec="$?";
    rm -f "$res_file";
    return "$ec";
  };

  image_size="$(identify -format "%[width]x%[height]" "png:$input_file")";
  image_depth="$(identify -format "%z" "png:$input_file")";

  if [ ! "$output_file" ]; then
    output_file="$header_file_name";
  fi;
  verb_output_file_name="$output_file";
  if [ "$l_output_stdin" = "true" ]; then
    verb_output_file_name="<stdout>";
  fi;

  verb_message "\
decoding file '$verb_input_file_name' to '$verb_output_file_name'
";

  if [ "$header_is_gzip" = "1" ]; then
    verb_info_message="
gzip md5..........: $header_gzip_md5";
    verb_gziped="true";
  fi;
  verb_info_message="\
decoded successfuly
header............: $header
image size........: $image_size
image depth.......: $image_depth
header length.....: $header_length
header file name..: $header_file_name
header data length: $header_data_length
header gziped.....: $verb_gziped
header file md5...: $header_data_md5\
$verb_info_message
";

  if [ "$l_format" = "true" ]; then
    cat "$res_file";
    rm -f "$res_file";
    return 0;
  fi;

  dd if="$res_file" skip="$header_length" ibs=1 count="$header_data_length" of="$tmp_file" 2>/dev/null;
  rm -f "$res_file";  

  if [ "$header_is_gzip" = "1" ]; then
    check_md5="$(md5sum "$tmp_file" | awk '{printf "%s", $1}')";
    if [ "$check_md5" != "$header_gzip_md5" ]; then
      error_message "%s\n" "$0: WARNING: '$input_file': gzip md5 does not match";
    fi;
    cat "$tmp_file" | gzip -d > "$tmp_gzip_file";
    rm -f "$tmp_file";
    tmp_file="$tmp_gzip_file";
  fi;

  check_md5="$(md5sum "$tmp_file" | awk '{printf "%s", $1}')";
  if [ "$check_md5" != "$header_data_md5" ]; then
    error_message "%s\n" "$0: WARNING: '$input_file': file md5 does not match";
  fi;

  if [ "$l_output_stdin" = "true" ]; then
    cat "$tmp_file";
  else
    cat "$tmp_file" > "$output_file";
  fi;

  verb_message "$verb_info_message";

  if [ "$l_remove" = "true" ]; then
    rm -f "$p_input_file";
  fi;

  rm -f "$tmp_file";
)
};

# show info from header
show_info_file() {
(
  input_file="$1";
  res_file="";

  file_length="0";

  header="";
  header_length="0";
  header_file_name="";
  header_data_length="0";
  header_is_gzip="0";
  header_data_md5="";
  header_gzip_md5="";

  verb_gziped="false";

  res_file="$p_temp_dir/pngfy-$$-res";

  identify "png:$input_file" 2>/dev/null >/dev/null || {
    error_message "%s\n" "$0: $input_file: not a valid png file";
    return 1;
  };

  convert -depth "$n_depth" "png:$input_file" "rgb:$res_file" || {
    error_message "%s\n" "$0: $input_file: not a valid png file";
    return 1;
  };

  read_header "$s_special_header" || {
    ec="$?";
    rm -f "$res_file";
    return "$ec";
  };

  image_size="$(identify -format "%[width]x%[height]" "png:$input_file")";
  image_depth="$(identify -format "%z" "png:$input_file")";

  if [ "$header_is_gzip" = "1" ]; then
    verb_info_message="
gzip md5..........: $header_gzip_md5";
    verb_gziped="true";
  fi;
  verb_info_message="\
header............: $header
image size........: $image_size
image depth.......: $image_depth
header length.....: $header_length
header file name..: $header_file_name
header data length: $header_data_length
header gziped.....: $verb_gziped
header file md5...: $header_data_md5\
$verb_info_message
";
  printf "%s" "$verb_info_message";

  if [ "$l_remove" = "true" ]; then
    rm -f "$p_input_file";
  fi;

  rm -f "$res_file";
)
};

# encode to image from file with header
format_encode() {
(
  input_file="$1";
  output_file="$2";
  res_file="$input_file";

  file_length="0";

  image_depth="$n_depth";
  image_bpp="$n_bpp";

  header="";
  header_length="0";
  header_file_name="";
  header_data_length="0";
  header_is_gzip="0";
  header_data_md5="";
  header_gzip_md5="";
  
  verb_gziped="false";
  verb_input_file_name="$input_file";
  verb_output_file_name="$output_file";

  if [ "$l_input_stdin" = "true" ]; then
    verb_input_file_name="<stdin>";
  fi;

  if [ "$l_output_stdin" = "true" ]; then
    verb_output_file_name="<stdin>";
  fi;

  verb_message "\
encoding file '$verb_input_file_name' to '$verb_output_file_name'
";

  read_header "$s_special_header" || {
    return "$?";
  };

  tmp_pixels="$(($file_length / $image_bpp))";
  tmp_pixels_sqrt="$(printf "%d" "$tmp_pixels" | awk '{printf "%d", sqrt($1);}')";
  image_x="$tmp_pixels_sqrt";
  image_y="$tmp_pixels_sqrt";

  image_pixels="$(($image_x * $image_y))";
  while [ "$image_pixels" -lt "$tmp_pixels" ]; do
    image_x="$(($image_x + 1))";
    image_pixels="$(($image_x * $image_y))";
  done;

  if [ "$header_is_gzip" = "1" ]; then
    verb_info_message="
gzip md5..........: $header_gzip_md5";
    verb_gziped="true";
  fi;
  verb_info_message="\
encoded successfuly
header............: $header
header length.....: $header_length
header file name..: $header_file_name
header data length: $header_data_length
header gziped.....: $verb_gziped
header file md5...: $header_data_md5\
$verb_info_message
";

  convert -size "${image_x}x${image_y}" -depth "$image_depth" "rgb:$res_file" "png:$output_file" && {
    verb_message "$verb_info_message";
    if [ "$l_remove" = "true" ]; then
      rm -f "$p_input_file";
    fi;
  }

  return 0;
)
};

# decode image to file with header
format_decode() {
(
  input_file="$1";
  output_file="$2";
  res_file="$input_file";

  file_length="0";

  tmp_file="$p_temp_dir/pngfy-$$-temp";
  tmp_gzip_file="$p_temp_dir/pngfy-$$-gzip";

  header="";
  header_length="0";
  header_file_name="";
  header_data_length="0";
  header_is_gzip="0";
  header_data_md5="";
  header_gzip_md5="";
  
  verb_gziped="false";
  verb_input_file_name="$input_file";

  if [ "$l_input_stdin" = "true" ]; then
    verb_input_file_name="<stdin>";
  fi;

  read_header "$s_special_header" || {
    return "$?";
  };

  if [ "$header_is_gzip" = "1" ]; then
    verb_info_message="
gzip md5..........: $header_gzip_md5";
    verb_gziped="true";
  fi;
  verb_info_message="\
decoded successfuly
header............: $header
header length.....: $header_length
header file name..: $header_file_name
header data length: $header_data_length
header gziped.....: $verb_gziped
header file md5...: $header_data_md5\
$verb_info_message
";

  dd if="$res_file" skip="$header_length" ibs=1 count="$header_data_length" of="$tmp_file" 2>/dev/null;

  if [ "$header_is_gzip" = "1" ]; then
    check_md5="$(md5sum "$tmp_file" | awk '{printf "%s", $1}')";
    if [ "$check_md5" != "$header_gzip_md5" ]; then
      error_message "%s\n" "$0: WARNING: '$input_file': gzip md5 does not match";
    fi;
    cat "$tmp_file" | gzip -d > "$tmp_gzip_file";
    rm -f "$tmp_file";
    tmp_file="$tmp_gzip_file";
  fi;

  check_md5="$(md5sum "$tmp_file" | awk '{printf "%s", $1}')";
  if [ "$check_md5" != "$header_data_md5" ]; then
    error_message "%s\n" "$0: WARNING: '$input_file': file md5 does not match";
  fi;

  if [ "$l_output_stdin" = "true" ]; then
    cat "$tmp_file";
  else
    cat "$tmp_file" > "$output_file";
  fi;

  verb_message "$verb_info_message";

  if [ "$l_remove" = "true" ]; then
    rm -f "$p_input_file";
  fi;

  rm -f "$tmp_file";

  return 0;
)
};

# check if file with header is formed right
format_check() {
(
  input_file="$1";
  res_file="$input_file";

  file_length="0";

  header="";
  header_length="0";
  header_file_name="";
  header_data_length="0";
  header_is_gzip="0";
  header_data_md5="";
  header_gzip_md5="";

  verb_gziped="false";
  verb_input_file_name="$input_file";

  if [ "$l_input_stdin" = "true" ]; then
    verb_input_file_name="<stdin>";
  fi;

  read_header "$s_special_header" || {
    return "$?";
  };

  if [ "$header_is_gzip" = "1" ]; then
    verb_info_message="
gzip md5..........: $header_gzip_md5";
    verb_gziped="true";
  fi;
  verb_info_message="\
file '$verb_input_file_name' is properly formed
header............: $header
header length.....: $header_length
header file name..: $header_file_name
header data length: $header_data_length
header gziped.....: $verb_gziped
header file md5...: $header_data_md5\
$verb_info_message
";

  if [ "$l_remove" = "true" ]; then
    rm -f "$p_input_file";
  fi;

  verb_message "$verb_info_message";
  return 0;
)
};

# chickens
main() {
  init || { return "$?"; };
  parse_args "$@" || { return "$?"; };
(
  case "$l_mode" in
    ("help") help; return 0; ;;
    ("encode") encode_file "$p_input_file" "$p_output_file" || { return "$?"; }; ;;
    ("decode") decode_file "$p_input_file" "$p_output_file" || { return "$?"; }; ;;
    ("info") show_info_file "$p_input_file" || { return "$?"; }; ;;
    ("format-encode") format_encode "$p_input_file" "$p_output_file" || { return "$?"; }; ;;
    ("format-decode") format_decode "$p_input_file" "$p_output_file" || { return "$?"; }; ;;
    ("format-check") format_check "$p_input_file" || { return "$?"; }; ;;
  esac;
)
};

# six lines of real code
main "$@" 
ec="$?";
for suffix in "temp" "res" "stdin" "gzip"; do
  [ -e "$p_temp_dir/pngfy-$$-$suffix" ] && { rm -f "$p_temp_dir/pngfy-$$-$suffix"; printf "%s\n" "$0: $suffix file has not been deleted"; };
done;
exit "$ec";
